using MongoDB.Driver;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ScreeningTool;
using Sustainalytics.ScreeningTool.Synchronization.Caches;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Sustainalytics.ScreeningTool.Synchronization.Handlers
{
    public class CorporateDataHandler : IHandler<CorporateData>
    {
        private readonly ResearchStatesCache _researchStatesCache;
        private readonly DsOnlyCompaniesCache _dsOnlyCompaniesCache;
        private readonly TearSheetCompanyIdsCache _tearSheetCompanyIdsCache;

        public CorporateDataHandler(
            ResearchStatesCache researchStatesCache,
            DsOnlyCompaniesCache dsOnlyCompaniesCache,
            TearSheetCompanyIdsCache tearSheetCompanyIdsCache)
        {
            _researchStatesCache = researchStatesCache;
            _dsOnlyCompaniesCache = dsOnlyCompaniesCache;
            _tearSheetCompanyIdsCache = tearSheetCompanyIdsCache;
        }

        public ProjectionDefinition<CorporateData> GetProjection(ProjectionDefinitionBuilder<CorporateData> builder) => builder
            .Include(x => x.MainIdentifiers)
            .Include(x => x.Timestamps)

            .Include(x => x.ComprehensiveEsgResearchState)
            .Include(x => x.CoreEsgResearchState)
            .Include(x => x.RiskRatingResearchState)
            .Include(x => x.ProductInvolvementResearchState)
            .Include(x => x.GccsResearchState)
            .Include(x => x.CorporateGovernanceResearchState)
            .Include(x => x.ControversyResearchState)

            .Include(x => x.TotalEmployees)
            .Include(x => x.CompanyType)
            .Include(x => x.Country)
            .Include(x => x.SustainalyticsSector)
            .Include(x => x.SustainalyticsSubIndustry)
            .Include(x => x.PeerGroup)
            .Include(x => x.SubPeerGroup)
            .Include(x => x.SubPeerGroupId)
            .Include(x => x.Region)
            .Include(x => x.MainExchangeTicker)
            .Include(x => x.MainExchange)
            .Include(x => x.MainTicker)
            .Include(x => x.PublishedMic)
            .Include(x => x.CountryOfExchangeCode)

            .Include(x => x.CapitalIqId)
            .Include(x => x.CompanyName)
            .Include(x => x.CompanyNameUrlFriendly)
            .Include(x => x.LastControversyUpdate)
            .Include(x => x.BusinessDescription)
            .Include(x => x.ResearchParentId);

        public IEnumerable<ExtraElement> GetExtraElements(CorporateData entity)
        {
            var timestamps = entity.Timestamps ?? new CorporateDataTimestamps();
            return EnumerableExtensions.Concat(
                GetExtraElements(entity, timestamps),
                GetExtraElements(entity.MainIdentifiers, timestamps),
                GetReportNameExtraElements(entity));
        }

        public IEnumerable<ExtraElement> GetExtraElements(CorporateData x, CorporateDataTimestamps t)
            => new ExtraElementList
            {
                { "Total Employees",              x.TotalEmployees,          t.TotalEmployees        },
                { "Company type",                 x.CompanyType,             t.CompanyType           },
                { "Country",                      x.Country,                 t.Country               },
                { "Sector",                       x.SustainalyticsSector,    t.SustainalyticsSector  },
                { "Subindustry",                  x.SubPeerGroup,            t.SubPeerGroup          },
                { "SubindustryId",                x.SubPeerGroupId,          t.SubPeerGroupId        },
                { "Peer Group",                   x.PeerGroup,               t.PeerGroup             },
                { "Region",                       x.Region,                  t.Region                },
                { "Exchange",                     x.MainExchange,            t.MainExchangeTicker    },
                { "Ticker",                       x.MainTicker,              t.MainExchangeTicker    },
                { "Exchange:Ticker",              x.MainExchangeTicker,      t.MainExchangeTicker    },
                { "MIC",                          x.PublishedMic,            t.PublishedMic          },
                { "Country of Exchange Code",     x.CountryOfExchangeCode,   t.CountryOfExchangeCode },

                { "Company Id",                   x.CapitalIqId                                      },
                { "Research Type",                x.GetResearchType(_researchStatesCache)            },
                { "Research Type-RR",             x.GetResearchTypeRR(_researchStatesCache)          },
                { "Company Name",                 x.CompanyName                                      },
                { "Company overview page URL",    x.GetCompanyOverviewPageUrl(_dsOnlyCompaniesCache) },
                { "Research Entity Name",         x.CompanyName                                      },
                { "Research Entity ID",           x.CapitalIqId                                      },
                { "Entity Type",                  x.GetEntityType()                                  },
                { "Date last controversy update", x.LastControversyUpdate.NullIfMinValue()           },
                { "Business Description",         x.BusinessDescription                              },
            };

        public IEnumerable<ExtraElement> GetExtraElements(MainIdentifiers x, CorporateDataTimestamps t)
            => x == null ? null : new ExtraElementList
            {
                { "Bloomberg Global Composite ID", x.BloombergGlobalCompositeId, t.BloombergGlobalCompositeId  },
                { "Bloomberg Global ID",           x.BloombergGlobalId,          t.BloombergGlobalId           },
                { "Bloomberg Share Class BBGID",   x.BloombergShareClassBBGId,   t.BloombergShareClassBBGId    },
                { "Bloomberg Ticker",              x.BloombergTicker,            t.BloombergTicker             },
                { "Bloomberg Ticker Exchange",     x.BloombergTickerExchange,    t.BloombergTickerExchange     },
                { "Bloomberg Unique ID",           x.BloombergUniqueId,          t.BloombergUniqueId           },
                { "CUSIP",                         x.CUSIP,                      t.CUSIP                       },
                { "ISIN",                          x.ISIN,                       t.ISIN                        },
                { "Legal Entity Identifier",       x.LegalEntityIdentifier,      t.LegalEntityIdentifier       },
                { "Sedol",                         x.Sedol,                      t.Sedol                       },
                { "SustIsin",                      x.SustIsin,                   t.SustIsin                    },
                { "Factset EntityId",              x.FactSetEntityId,            t.FactSetEntityId             },
                { "Factset SecurityId",            x.FactSetSecurityId,          t.FactSetSecurityId           },
                { "Morningstar CompanyId",         x.MorningstarCompanyId,       t.MorningstarCompanyId        },
                { "Glass Lewis CompanyId",         x.GlassLewisCompanyId,        t.GlassLewisCompanyId         },
                { "CompanyId_old",                 x.CompanyId_old,              t.CompanyId_old               },
            };

        public IEnumerable<ExtraElement> GetReportNameExtraElements(CorporateData entity)
        {
            var researchStates = _researchStatesCache.GetResearchStates(entity.Id);

            if (researchStates == null)
                return Enumerable.Empty<ExtraElement>();

            return GetReportNameExtraElements(entity, researchStates);
        }

        public IEnumerable<ExtraElement> GetReportNameExtraElements(CorporateData entity, ResearchStates researchStates)
            => _dsOnlyCompaniesCache.Contains(entity.Id) ? null : new ExtraElementList
            {
                { entity.GetReportExtraElement(researchStates.ComprehensiveEsg,    "Comprehensive TearSheet",           _tearSheetCompanyIdsCache)         },
                { entity.GetReportExtraElement(researchStates.ComprehensiveEsg,    "Comprehensive Company ESG",         ReportName.ESGRatings)             },
                { entity.GetReportExtraElement(researchStates.ComprehensiveEsg,    "Comprehensive Summary ESG",         ReportName.ESGSummary)             },
                { entity.GetReportExtraElement(researchStates.CoreEsg,             "Core Company ESG",                  ReportName.ESGRatings)             },
                { entity.GetReportExtraElement(researchStates.CoreEsg,             "Core Summary ESG",                  ReportName.ESGSummary)             },
                { entity.GetReportExtraElement(researchStates.RiskRating,          "Comprehensive Company Risk Rating", ReportName.RiskRatings)            },
                { entity.GetReportExtraElement(researchStates.RiskRating,          "Comprehensive Summary Risk Rating", ReportName.RiskRatingsSummary)     },
                { entity.GetReportExtraElement(researchStates.CoreRiskRating,      "Core Company Risk Rating",          ReportName.CoreRiskRatings)        },
                { entity.GetReportExtraElement(researchStates.CoreRiskRating,      "Core Summary Risk Rating",          ReportName.CoreRiskRatingsSummary) },
                { entity.GetReportExtraElement(researchStates.Controversy,         "Company Controversy",               ReportName.Controversy)            },
                { entity.GetReportExtraElement(researchStates.CorporateGovernance, "Company Corporate Governance",      ReportName.CG)                     },
                { entity.GetReportExtraElement(researchStates.ProductInvolvement,  "Company Product Involvement",       ReportName.PI)                     },
            };
    }

    public static class CorporateDataExtensions
    {
        public static string GetResearchType(this CorporateData entity, ResearchStatesCache researchStatesCache)
        {
            if (researchStatesCache.IsResearchAvailable(entity.Id, x => x.ComprehensiveEsg))
                return "Comprehensive";

            if (researchStatesCache.IsResearchAvailable(entity.Id, x => x.CoreEsg))
                return "Core";

            return null;
        }

        public static string GetResearchTypeRR(this CorporateData entity, ResearchStatesCache researchStatesCache)
        {
            if (researchStatesCache.IsResearchAvailable(entity.Id, x => x.RiskRating))
                return "Comprehensive";

            if (researchStatesCache.IsResearchAvailable(entity.Id, x => x.CoreRiskRating))
                return "Core";

            return null;
        }

        public static string GetCompanyOverviewPageUrl(this CorporateData entity, DsOnlyCompaniesCache dsOnlyCompaniesCache)
        {
            if (dsOnlyCompaniesCache.Contains(entity.Id))
                return null;

            return $"https://globalaccess.sustainalytics.com/#/research/company/{entity.CompanyNameUrlFriendly}/overview";
        }

        public static string GetEntityType(this CorporateData entity)
            => entity.ResearchParentId != null ? "Coverage" : "Research";

        public static ExtraElement GetReportExtraElement(this CorporateData entity, ResearchState researchState, string key, TearSheetCompanyIdsCache _tearSheetCompanyIdsCache)
        {
            if (!_tearSheetCompanyIdsCache.Contains(entity.Id))
                return null;

            return GetReportExtraElement(researchState, key, $"{entity.CapitalIqId}.pdf");
        }

        public static ExtraElement GetReportExtraElement(this CorporateData entity, ResearchState researchState, string key, ReportName reportName)
            => GetReportExtraElement(researchState, key, reportName.GetFullReportName(entity.CompanyName, DateTime.UtcNow));

        public static ExtraElement GetReportExtraElement(ResearchState researchState, string key, string reportName)
            => researchState.IsAvailable() ? new ExtraElement($"{key} PDF report", reportName) : null;
    }
}
